#!/usr/bin/env python3
# -*- coding: utf-8 -*-

""" 文件描述内容 """

__auther__ = 'igofishing'


import numpy as np
import matplotlib.pyplot as plt


pop_size = 200      # 个体数量
DNA_size = 10       # DNA 长度

fit_interval = [0, 5]  # 横坐标区间
cross_rate = 0.8    # 结合的概率
mutation_rate = 0.03    # 变异的概率


def get_pop():
    """ 获取随机pop """
    return np.random.randint(2, size=(pop_size, DNA_size))


def translate(pop):
    """ 翻译DNA """
    interval_len = fit_interval[1] - fit_interval[0]
    return pop.dot(2 ** np.arange(DNA_size)[::-1]) / float(2**DNA_size-1) * interval_len + fit_interval[0]


def source(x):
    """ 需要检测的目标函数 """
    return np.sin(10 * x) * x + np.cos(2 * x) * x


def get_fitness(fitness):
    """ 获取群体对应的适应值 """
    return fitness + 1e-3 - np.min(fitness)


def crossover(father, mother):
    """ 根据父代适应度产生新群体 """
    if np.random.rand() < cross_rate:
        # 得到一个基因上的随机坐标
        idx = np.random.randint(DNA_size)
        cross_points = np.random.randint(0, 2, size=DNA_size).astype(np.bool)
        father[cross_points] = mother[idx, cross_points]
    return father


def select(pop, fitness):
    """ 根据个体的适应值选择保留基因的个体 """
    idx = np.random.choice(np.arange(pop_size), size=pop_size, replace=True,
                           p=fitness/np.sum(fitness))
    return pop[idx]


def mutate(child):
    for point in range(DNA_size):
        if np.random.rand() < mutation_rate:
            child[point] = 1 if child[point] == 0 else 0
    return child


if __name__ == '__main__':
    # 画图功能
    plt.ion()  # something about plotting
    x = np.linspace(*fit_interval, 200)
    plt.plot(x, source(x))

    # 获取初始群体的DNA
    pop = get_pop()
    for i in range(pop_size):

        # 获取实数基因
        real = translate(pop)
        print(real)
        # 获取实际坐标
        points = source(real)

        # something about plotting
        if 'sca' in globals(): sca.remove()
        sca = plt.scatter(real, points, s=200, lw=0, c='red', alpha=0.5)
        plt.pause(0.05)

        fitness = get_fitness(points)
        pop = select(pop, fitness)
        pop_copy = pop.copy()
        for parent in pop:
            child = crossover(parent, pop_copy)
            child = mutate(child)
            parent[:] = child  # parent is replaced by its child

plt.ioff()
plt.show()
